package com.isyscore.os.etl.manager;


import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.isyscore.os.core.exception.DataFactoryException;
import com.isyscore.os.core.exception.ErrorCode;

import com.isyscore.os.etl.model.dto.DataSourceDTO;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.dbutils.DbUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * @author fjZheng
 * @version 1.0
 * @date 2020/11/20 14:14
 */
@Component
@Slf4j
public class MysqlDataSourceManager {

    private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";

    private static final String URL_TEMPLATE = "jdbc:mysql://%s:%s/%s?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&useSSL=false";

    /**
     * 5分钟过期
     */
    private final Cache<String, DataSource> datasourceMd5Map = Caffeine.newBuilder()
            .maximumSize(3)
            .expireAfterAccess(5, TimeUnit.MINUTES)
            .removalListener((key, value, cause) -> {
                // 过期移除datasource时需要显式关闭pool.
                if (Objects.nonNull(value)) {
                    ((HikariDataSource) value).close();
                }
            }).build();

    public DataSource getDataSource(DataSourceDTO datasourceDTO) throws SQLException {
        String jdbcUrl = getJdbcUrl(datasourceDTO);
        String md5Str = jdbcUrl + datasourceDTO.getUserName() + datasourceDTO.getPassword();
        String md5 = DigestUtils.md5DigestAsHex(md5Str.getBytes());
        if (datasourceMd5Map.asMap().containsKey(md5)) {
            return datasourceMd5Map.getIfPresent(md5);
        }
        final HikariConfig config = new HikariConfig();
        config.setConnectionTimeout(SECONDS.toMillis(10));
        config.setUsername(datasourceDTO.getUserName());
        config.setPassword(datasourceDTO.getPassword());
        config.setDriverClassName(DRIVER_CLASS);
        config.setJdbcUrl(jdbcUrl);
        config.setMaximumPoolSize(3);
        final HikariDataSource dataSource = new HikariDataSource(config);
        datasourceMd5Map.put(md5, dataSource);
        return dataSource;
    }

    private String getJdbcUrl(DataSourceDTO datasourceDTO) {
        return String.format(URL_TEMPLATE, datasourceDTO.getIp(),
                Integer.parseInt(datasourceDTO.getPort()),
                datasourceDTO.getSelectDatabaseName());
    }

    /**
     * 获取连接
     *
     * @param datasourceDTO
     * @return
     */
    public Connection getConn(DataSourceDTO datasourceDTO) {
        try {
            DataSource dataSource = getDataSource(datasourceDTO);
            if (null == dataSource) {
                return null;
            }
            Connection con = dataSource.getConnection();
            return con;
        } catch (SQLException e) {
            log.error("data source connection error", e);
            throw new DataFactoryException(ErrorCode.DATA_SOURCE_CONNECTION_ERROR);
        }
    }

    /**
     * 关闭连接
     *
     * @param conn
     * @param ps
     * @param rs
     * @Author fjzheng
     */
    public  void close(Connection conn, Statement ps, ResultSet rs) {
        DbUtils.closeQuietly(conn, ps, rs);
    }


}
